package com.starry.module.system.core.role.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.starry.core.common.constants.CommonError;
import com.starry.core.common.domain.vo.PageVo;
import com.starry.core.common.execption.ServiceException;
import com.starry.core.common.tenant.context.TenantContextHolder;
import com.starry.core.common.utils.CollectionUtils;
import com.starry.core.security.context.SecurityInfoContext;
import com.starry.core.security.domain.BaseUser;
import com.starry.core.tenant.config.TenantProperties;
import com.starry.db.core.enums.DBEnableDisableStatusEnum;
import com.starry.db.core.query.LambdaQueryWrapperX;
import com.starry.module.system.api.role.domain.qo.SysRolePageQo;
import com.starry.module.system.api.role.domain.qo.SysRoleQo;
import com.starry.module.system.api.role.domain.vo.SysRoleDtlVo;
import com.starry.module.system.api.role.domain.vo.SysRoleVo;
import com.starry.module.system.core.dept.service.SysDeptService;
import com.starry.module.system.core.menu.service.SysMenuService;
import com.starry.module.system.core.role.entity.SysRole;
import com.starry.module.system.core.role.mapper.SysRoleMapper;
import com.starry.module.system.core.role.service.SysRoleService;
import com.starry.module.system.core.tenant.service.SysTenantService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.starry.module.system.core.enums.ErrorCodeConstants.MENU_NOT_EXISTS;
import static com.starry.module.system.core.enums.ErrorCodeConstants.TENANT_NOT_MENU_PERMISSION;

/**
 * 系统角色-服务实现
 *
 * @author xiaoke
 */
@Service("sysRoleService")
public class SysRoleServiceImpl implements SysRoleService {

    private final SysRoleMapper sysRoleMapper;
    private final SysDeptService sysDeptService;
    private final SysMenuService sysMenuService;
    private final SysTenantService sysTenantService;
    private final TenantProperties tenantProperties;

    public SysRoleServiceImpl(SysRoleMapper sysRoleMapper, SysDeptService sysDeptService, SysMenuService sysMenuService, SysTenantService sysTenantService, TenantProperties tenantProperties) {
        this.sysRoleMapper = sysRoleMapper;
        this.sysDeptService = sysDeptService;
        this.sysMenuService = sysMenuService;
        this.sysTenantService = sysTenantService;
        this.tenantProperties = tenantProperties;
    }

    @Override
    public PageVo<SysRoleVo> page(SysRolePageQo pageQo) {
        PageVo<SysRole> page = sysRoleMapper.selectPage(pageQo);
        List<SysRoleVo> vos = BeanUtil.copyToList(page.getRows(), SysRoleVo.class);
        return new PageVo<>(vos, page.getTotal());
    }

    @Override
    public SysRoleDtlVo getById(Long id) {
        SysRoleDtlVo sysRole = sysRoleMapper.selectByIdDetail(id);
        if (sysRole == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        // 数据范围处理转成Set
        sysRole.setDataScopeDeptList(CollectionUtils.convertStringToSet(sysRole.getDataScopeDeptIds()));
        return sysRole;
    }

    @Transactional
    @Override
    public Long save(SysRoleQo sysRoleQo) {
        LambdaQueryWrapperX<SysRole> wrapper = new LambdaQueryWrapperX<>();
        wrapper
                .eq(SysRole::getName, sysRoleQo.getName());
        if (sysRoleMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }

        // 校验机构是否存在
        validateDeptExists(sysRoleQo.getDataScopeDeptIds());
        // 校验菜单是否存在
        validateMenuExists(sysRoleQo.getMenuIds());
        //校验当前租户套餐是否有菜单权限
        validateMenuPermission(sysRoleQo.getMenuIds());

        SysRole sysRole = BeanUtil.toBean(sysRoleQo, SysRole.class);
        sysRole.setId(null);
        sysRoleMapper.insert(sysRole);

        //插入菜单权限关联
        insertRoleMenu(sysRole.getId(), sysRoleQo.getMenuIds());

        return sysRole.getId();
    }

    @Override
    public boolean existRoleList(Set<Long> roleIdList) {
        if (roleIdList.isEmpty()) return true;
        LambdaQueryWrapperX<SysRole> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper
                .in(SysRole::getId, roleIdList);
        return sysRoleMapper.selectCount(queryWrapper) == roleIdList.size();
    }

    @Override
    public boolean exitsRoleListStaus(Set<Long> roleIdList) {
        if (roleIdList.isEmpty()) return true;
        LambdaQueryWrapperX<SysRole> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper
                .in(SysRole::getId, roleIdList)
                .eq(SysRole::getStatus, DBEnableDisableStatusEnum.ENABLE.getCode());
        return sysRoleMapper.selectCount(queryWrapper) == roleIdList.size();
    }

    @Override
    public List<SysRoleVo> getByUserIdList(Long id) {
        return sysRoleMapper.selectByUserIdRoleList(id);
    }

    @Transactional
    @Override
    public void updateById(SysRoleQo sysRoleQo) {
        if (sysRoleQo.getId() == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        LambdaQueryWrapperX<SysRole> wrapper = new LambdaQueryWrapperX<>();
        wrapper
                .ne(SysRole::getId, sysRoleQo.getId())
                .and(w ->
                        w.eq(SysRole::getName, sysRoleQo.getName())
                );
        if (sysRoleMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }

        // 校验机构是否存在
        validateDeptExists(sysRoleQo.getDataScopeDeptIds());
        // 校验菜单是否存在
        validateMenuExists(sysRoleQo.getMenuIds());
        //校验当前租户套餐是否有菜单权限
        validateMenuPermission(sysRoleQo.getMenuIds());

        SysRole sysRole = sysRoleMapper.selectById(sysRoleQo.getId());
        if (sysRole == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        BeanUtil.copyProperties(sysRoleQo, sysRole);
        sysRoleMapper.updateById(sysRole);

        //更新菜单权限关联
        updateRoleMenu(sysRole.getId(), sysRoleQo.getMenuIds());
    }

    @Transactional
    @Override
    public void removeByIds(Set<Long> idList) {
        if (idList.isEmpty()) return;
        List<SysRole> roleList = sysRoleMapper.selectByIdsIsUseRole(idList);
        if (!roleList.isEmpty()) {
            List<String> codes = roleList.stream()
                    .map(SysRole::getCode)
                    .toList();
            throw new ServiceException(String.format("角色编号%s正在被使用，无法执行删除操作", codes));
        }
        sysRoleMapper.deleteByIds(idList);
    }

    @Transactional
    @Override
    public void enable(Set<Long> idList) {
        if (idList.isEmpty()) return;
        sysRoleMapper.updateBatch(setsysRoleStatus(idList, DBEnableDisableStatusEnum.ENABLE));
    }

    @Transactional
    @Override
    public void disable(Set<Long> idList) {
        if (idList.isEmpty()) return;
        sysRoleMapper.updateBatch(setsysRoleStatus(idList, DBEnableDisableStatusEnum.DISABLE));
    }

    @Override
    public List<SysRoleVo> findList(SysRolePageQo pageQo) {
        List<SysRole> sysRoles = sysRoleMapper.selectRoleList(pageQo);
        return BeanUtil.copyToList(sysRoles, SysRoleVo.class);
    }

    @Override
    public Long saveTenantRoleAdmin(Set<Long> menuIdList) {
        SysRole sysRole = new SysRole();
        sysRole.setTenantRoleAdmin(true);
        sysRole.setName("租户管理员");
        sysRole.setCode("TENANT_ADMIN");
        sysRole.setRemark("拥有租户全部权限");

        sysRoleMapper.insert(sysRole);

        //插入菜单权限关联
        insertRoleMenu(sysRole.getId(), menuIdList);

        return sysRole.getId();
    }

    /**
     * 设置角色状态
     *
     * @param idList 角色Id列表
     * @param status 状态
     * @return 角色列表
     */
    private Set<SysRole> setsysRoleStatus(Set<Long> idList, DBEnableDisableStatusEnum status) {
        Set<SysRole> sysRoles = new HashSet<>();
        idList.forEach(item -> {
            SysRole sysRole = new SysRole();
            sysRole.setId(item);
            sysRole.setStatus(status.getCode());
            sysRoles.add(sysRole);
        });
        return sysRoles;
    }

    /**
     * 插入角色菜单关联
     *
     * @param role    角色Id
     * @param menuIds 菜单Id列表
     */
    private void insertRoleMenu(Long role, Set<Long> menuIds) {
        if (menuIds.isEmpty()) return;
        BaseUser loginUser = SecurityInfoContext.getLoginUser();
        sysRoleMapper.insertRoleMenu(role, menuIds, loginUser.getId(), loginUser.getUsername());
    }

    /**
     * 删除角色菜单关联
     *
     * @param role    角色Id
     * @param menuIds 菜单Id列表
     */
    private void deleteRoleMenu(Long role, Set<Long> menuIds) {
        sysRoleMapper.deleteRoleMenu(role, menuIds);
    }

    /**
     * 更新角色菜单关联
     *
     * @param id         角色Id
     * @param menuIdList 菜单Id列表
     */
    private void updateRoleMenu(Long id, Set<Long> menuIdList) {
        Set<Long> dbMenuIds = sysRoleMapper.getRoleMenuIds(id);
        // 计算新增和删除的菜单编号
        Set<Long> menuIds = CollUtil.emptyIfNull(menuIdList);
        Collection<Long> createPostIds = CollUtil.subtract(menuIds, dbMenuIds);
        Collection<Long> deletePostIds = CollUtil.subtract(dbMenuIds, menuIds);

        // 执行新增和删除。对于已经授权的菜单，不用做任何处理
        if (!CollectionUtil.isEmpty(createPostIds)) {
            BaseUser loginUser = SecurityInfoContext.getLoginUser();
            sysRoleMapper.insertRoleMenu(id, new HashSet<>(createPostIds), loginUser.getId(), loginUser.getUsername());
        }
        if (!CollectionUtil.isEmpty(deletePostIds)) {
            deleteRoleMenu(id, new HashSet<>(deletePostIds));
        }
    }

    /**
     * 校验当前租户套餐是否有菜单权限
     *
     * @param menuIds 菜单id集合
     */
    private void validateMenuPermission(Set<Long> menuIds) {
        if (tenantProperties.getEnable()) {
            if (TenantContextHolder.validateIsSystemTenant()) {
                return;
            }
            Long tenantId = TenantContextHolder.getRequiredTenantId();
            Set<Long> tenantPackageMenuIds = sysTenantService.getTenantPackageMenuIds(tenantId);
            if (!tenantPackageMenuIds.containsAll(menuIds)) {
                throw new ServiceException(TENANT_NOT_MENU_PERMISSION);
            }
        }
    }


    /**
     * 校验机构是否存在
     *
     * @param deptList 机构Id列表
     */
    private void validateDeptExists(Set<Long> deptList) {
        if ((deptList != null && !deptList.isEmpty()) && !sysDeptService.existDeptList(deptList)) {
            throw new ServiceException(CommonError.DEPT_NOT_EXISTS.getMsg());
        }
    }

    /**
     * 校验菜单是否存在
     *
     * @param menuIds 菜单Id列表
     */
    private void validateMenuExists(Set<Long> menuIds) {
        if (!sysMenuService.existMenuList(menuIds)) {
            throw new ServiceException(MENU_NOT_EXISTS);
        }
    }

}




